package com.dream.banana.redis.lock;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import com.dream.banana.redis.lock.annotation.DistributedLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
 * 躲了一辈子雨，雨会不会很难过。
 * </p>
 * Created by zhumenglong on 2017/5/28.
 */
@Aspect
public class LockAspect {
    private static Logger logger = LoggerFactory.getLogger(LockAspect.class);
    private RedissonClient client;

    @Pointcut("@annotation(com.vpiaotong.redis.lock.annotation.DistributedLock)")
    public void getDistributedLock() {
    }

    @Around("getDistributedLock()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        Method method = this.getMethod(pjp);
        DistributedLock lockAnno = (DistributedLock) method.getAnnotation(DistributedLock.class);
        String key = lockAnno.keyPrefix().concat("_")
                .concat(this.parseKey(lockAnno.keySuffix(), method, pjp.getArgs()));
        RLock lock = this.client.getLock(key);

        Object arg6;
        try {
            boolean isGetLock = true;
            lock.tryLock((long) lockAnno.waitLockTime(), (long) lockAnno.expireTime(), TimeUnit.MILLISECONDS);

            while (isGetLock) {
                if (lock.isLocked()) {
                    isGetLock = false;
                }
            }

            arg6 = pjp.proceed();
        } finally {
            lock.unlock();
        }

        return arg6;
    }

    private Method getMethod(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        Class[] argTypes = new Class[pjp.getArgs().length];

        for (int method = 0; method < args.length; ++method) {
            argTypes[method] = args[method].getClass();
        }

        Method arg7 = null;

        try {
            arg7 = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argTypes);
        } catch (NoSuchMethodException arg5) {
            logger.error(arg5.getMessage());
            arg5.printStackTrace();
        } catch (SecurityException arg6) {
            logger.error(arg6.getMessage());
            arg6.printStackTrace();
        }

        return arg7;
    }

    private String parseKey(String key, Method method, Object[] args) {
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        SpelExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();

        for (int i = 0; i < paraNameArr.length; ++i) {
            context.setVariable(paraNameArr[i], args[i]);
        }

        return (String) parser.parseExpression(key).getValue(context, String.class);
    }

    public void setClient(RedissonClient client) {
        this.client = client;
    }
}
